﻿/* 
 * Evo
 * 
 * Marcin Krupowicz (marcin@krupowicz.eu)
 * Barbara Szymanek (basia.szymanek@gmail.com)
 * 
 * January, 2009
 *
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace Evo
{
	/// <summary>
	/// Class representing population. Used by evolutionary algorithms for evaluating.
	/// </summary>
	/// <typeparam name="T">Type of individuals</typeparam>
	public class Population<T> where T : IIndividual
	{
		#region Private section
		private Random random = new Random(DateTime.Now.Millisecond);
		private List<T> individuals;
		#endregion

		#region Properties
		/// <summary>
		/// List of individuals
		/// </summary>
		public List<T> Individuals
		{
			get { return individuals; }
		}

		/// <summary>
		/// Size of population
		/// </summary>
		public int Size
		{
			get { return individuals.Count; }
		}

		/// <summary>
		/// Objective function, set for all individuals
		/// </summary>
        public EvaluateChromosomeDelegate ObjectiveFunction
        {
            set 
            { 
                foreach (T individual in individuals)
                    individual.ObjectiveFunction = value; 
       
            }
        }

		/// <summary>
		/// The best individual in population
		/// </summary>
		public T Best
		{
			get
			{
				T best = individuals[0];
				foreach (T ind in individuals)
				{
					if (ind.Value > best.Value)
						best = ind;
				}
				return best;
			}
		}

		/// <summary>
		/// The worst individual in population
		/// </summary>
		public T Worst
		{
			get
			{
				T worst = individuals[0];
				foreach (T ind in individuals)
				{
					if (ind.Value < worst.Value)
						worst = ind;
				}
				return worst;
			}
		}

		/// <summary>
		/// Average value of objective function in population
		/// </summary>
		public double Average
		{
			get
			{
				if (this.Size == 0)
					return 0.0;
				double sum = 0.0;
				foreach (T individual in this.Individuals)
					sum += individual.Value;
				return sum / this.Size;
			}
		}

		/// <summary>
		/// Standard deviation of objective function in population
		/// </summary>
		public double StandardDeviation
		{
			get
			{
				if (this.Size == 0)
					return 0.0;
				double avg = this.Average;
				double sum = 0.0;
				foreach (T individual in this.Individuals)
					sum += Math.Pow(individual.Value - avg, 2.0);
				return Math.Sqrt(sum / this.Size);
			}
		}
		#endregion

		#region constructor
		/// <summary>
		/// Create Population from list of individuals
		/// </summary>
		/// <param name="individuals">list of individuals</param>
		public Population(List<T> individuals)
		{
			this.individuals = individuals;
		}

		#endregion

		/// <summary>
		/// indexth individual in population (i.e. can be used after sort() method to get indexth individual)
		/// </summary>
		/// <param name="index">number of individual</param>
		/// <returns>Individual of number `index`</returns>
		public T this[int index]
		{
			get { return this.individuals[index]; }
			set { this.individuals[index] = value; }
		}

 
		/// <summary>
		/// Evaluate fitness values for all individuals. If objective function is not evaluated yet, 
		/// this method will at first also perform this operation.
		/// </summary>
		/// <param name="scalingType">Type of scaling to be used</param>
		public void Evaluate(ScalingType scalingType)
		{
			//Seting fitness value
			double minValue = this.Worst.Value;
			double sum = 0.0;
			foreach (T ind in this.individuals)
				sum += ind.Value - minValue;
			foreach (T ind in this.individuals)
			{
				if (sum > 0)
					ind.Fitness = (ind.Value - minValue) / sum;
				else
					ind.Fitness = 1.0/individuals.Count;
			}

			//Scaling
			switch (scalingType)
			{
				case ScalingType.None:
					break;
				case ScalingType.SimpleScaling:
					Scaling<T>.SimpleScaling(this);
					break;
				case ScalingType.PowerScaling:
					Scaling<T>.PowerScaling(this);
					break;
				case ScalingType.SigmaScaling:
					Scaling<T>.SimpleScaling(this);
					break;
				case ScalingType.ExponentialScaling:
					Scaling<T>.ExponentialScaling(this);
					break;
			}
		}

		/// <summary>
		/// Split population into two new populations with equal number of individuals (or nearly equal if Size is odd).
		/// </summary>
		/// <returns>Two half-size populations</returns>
		public Pair<Population<T>, Population<T>> RandomSplit()
		{
			int half_size = this.Size % 2 == 0 ? this.Size / 2 : this.Size / 2 + 1;
			List<T> list1 = new List<T>(half_size);
			List<T> list2 = new List<T>(half_size);

			for (int i = 0; i < this.Size; i++)
			{
				if (random.Next(0, 1) == 0)
					list1.Add(this.Individuals[i]);
				else
					list2.Add(this.Individuals[i]);
				if (list1.Count == half_size || list1.Count == half_size)
					break;
			}

			if (list1.Count == half_size)
				list2.AddRange(this.Individuals.GetRange(half_size, this.Size - half_size));
			else if (list2.Count == half_size)
				list1.AddRange(this.Individuals.GetRange(half_size, this.Size - half_size));

			return new Pair<Population<T>, Population<T>>(new Population<T>(list1), new Population<T>(list2));
		}

		/// <summary>
		/// Generate random population
		/// </summary>
		/// <param name="size">size of population</param>
		/// <param name="chromosomeLength">length of chromosome of each individual</param>
		/// <returns>New, randomly generated population</returns>
		public static Population<T> GenerateRandom(int size, int chromosomeLength)
		{
			List<T> individuals = new List<T>();
			for (int i = 0; i < size; i++)
			{
				T individual = (T)Activator.CreateInstance(typeof(T));
				individual.SetRandomChromosome(chromosomeLength);
				individuals.Add(individual);
			}
			return new Population<T>(individuals);
		}
	}
}
